#include "base_server.cpp"

//群聊服务器
//I/O模型采用select
//进程采用全局单进程

void group_chat(int sockfd, int maxi, int client[], fd_set rset,char buffer[]);

int main(){
    int MAXLINE = 256;
    int i, maxi, maxfd, listenfd, connfd, sockfd;
    char buffer[MAXLINE];

    //client表示可读的客户端fd，FD_SETSIZE默认1024
    int nready, client[FD_SETSIZE];
    ssize_t n;
    struct fd_set rset, allset;

    pid_t childPid;
    void sig_child(int), sig_init(int), web_child(int);
    socklen_t clilen, addrlen;
    struct sockaddr *cliaddr; 

    listenfd = tcp_create_bind_listen(GROUP_CHAT_PORT);

    signal(SIGCHLD, sig_child);
    signal(SIGINT, sig_init);

    maxfd = listenfd;
    maxi = -1;

    // 初始化所有可读的字符集，只包含conn的
    for(i = 0; i< FD_SETSIZE; i++){
        client[i] = -1;
    }

    // 初始化所有的字符集，包含listen和conn的
    FD_ZERO(&allset);

    // 设置listen字符集
    //参考Php的select版本，此行代码相当于$allset[] = $listenfd;
    FD_SET(listenfd, &allset);

    // 这个for应该 是无限循环，为了测试改为了有限数量
    // for(;;){
    for(int j = 0; j < 10000; j++){
        // select之前rset表示所有的字符集
        rset = allset;
        nready = select(maxfd + 1, &rset, NULL, NULL, NULL);//阻塞
        // select之后reset表示可读的字符集

        //处理新连接
        if(FD_ISSET(listenfd, &rset)){
            //new client coming
            clilen = sizeof(cliaddr);
            connfd = accept(listenfd, cliaddr, &clilen);

            // 把新connect上来的客户字符集放入client数组
            for(i = 0; i < FD_SETSIZE; i++){
                if(client[i] < 0){
                    client[i] = connfd;
                    break;
                }
            }
            if(i == FD_SETSIZE){
                error_func("已达连接数上限", n);
            }

            //把connect上来的客户字符集加入allset，达到复用的目的
            FD_SET(connfd, &allset);

            if(connfd > maxfd){
                maxfd = connfd;
            }

            // i表示client数组的下标，maxi表示最大的那个下标，也就是maxi + 1表示client数组的数量
            if(i > maxi){
                maxi = i;
            }

            if(--nready <= 0){
                continue;
            }
        }

        //处理读写
        for (i = 0; i <= maxi; i++){
            if((sockfd = client[i]) < 0){
                continue;
            }

            if(FD_ISSET(sockfd, &rset)){
                
                memset(buffer , 0 , MAXLINE);
                if((n = read(sockfd, buffer, MAXLINE)) == 0){
                    //closed by client
                    close(sockfd);
                    FD_CLR(sockfd, &allset);//类似unset($allset[$sockfd])
                    client[i] = -1;
                }else{
                    printf("read()=>%s\n", buffer);
                    // write(sockfd, buffer, n);
                    //群发给其他所有人
                    group_chat(sockfd, maxi, client, rset,  buffer);

                    printf("send()=>%s\n", buffer);
                }

                if(--nready <= 0){
                    break;
                }
            }
        }
        
    }

    return 0;
}

void sig_child(int signo){
    printf("sign handle SIGCHLD\n");
    wait(NULL);
}

void sig_init(int signo){
    printf("sign handle SIGINT\n");
    wait(NULL);
    sleep(1);
    exit(0);
}

void group_chat(int sockfd, int maxi, int client[], fd_set rset,char buffer[]){
    int client_socket;
    for(int i = 0; i <= maxi; i++){
        if((client_socket = client[i]) < 0){
            continue;
        }

        if(client_socket != sockfd){
            write(client_socket, buffer, 1024);
        }
    }
}

void web_child(int connfd){
    // sleep(3);

    char buffer[256];
    int n=0;
    bzero(buffer, 256);
    n = read(connfd, buffer, 255);
    if(n < 0){
        error_func("read socket失败", n);
    }else if(n == 0){
        //对方关闭
        error_func("对方关闭连接", n);
    }

    printf("read()=>%s\n", buffer);

    // char sendBuff[1025] = "你好呀，连接成功";
    write(connfd, buffer, strlen(buffer));
    printf("send()=>%s\n", buffer);
}

